using Admin.NET.Application.Entity;
using Admin.NET.Core;
using Furion.DependencyInjection;
using Microsoft.AspNetCore.Authorization;
using SqlSugar;

namespace Admin.NET.Application.Service.Report;

/// <summary>
/// 仓库汇总报表：按仓库汇总初期金额与末期金额，并统计货品种类数
/// 口径：
/// 初期金额 = 截止到(StartTime - 1)的(入库金额 - 出库金额)
/// 末期金额 = 截止到EndTime的(入库金额 - 出库金额)
/// 货品种类 = 截止到EndTime的该仓库出现过的不同货品名称计数
/// </summary>
[ApiDescriptionSettings("Report", Name = "warehouseSummary", Order = 905)]
[Route("api/report/warehouseSummary")]
[AllowAnonymous]
public class WarehouseSummaryReportService : IDynamicApiController, ITransient
{
	private readonly ISqlSugarClient _sqlSugarClient;

	public WarehouseSummaryReportService(ISqlSugarClient sqlSugarClient)
	{
		_sqlSugarClient = sqlSugarClient;
	}

	[HttpPost("page")]
	public async Task<object> Page([FromBody] GetWarehouseSummaryInput input)
	{
		var start = input.StartTime?.Date;
		var end = input.EndTime?.Date;
		DateTime? startCut = start?.AddDays(-1); // 初期截止：开始日前一天的 23:59:59.999...
		if (startCut != null) startCut = startCut.Value.AddDays(1).AddTicks(-1);
		if (end != null) end = end.Value.AddDays(1).AddTicks(-1);

		// 入库累计到某时点
		async Task<List<WAgg>> InAggUntil(DateTime? until)
		{
			var q = _sqlSugarClient.Queryable<Sysinstockorder>()
				.WhereIF(until != null, x => x.InDate <= until)
				.WhereIF(!string.IsNullOrWhiteSpace(input.WarehouseName), x => x.WarehouseName!.Contains(input.WarehouseName!))
				.WhereIF(!string.IsNullOrWhiteSpace(input.GoodsOrCategory), x => x.GoodsName!.Contains(input.GoodsOrCategory!))
				.GroupBy(x => x.WarehouseName)
				.Select(x => new WAgg { WarehouseName = x.WarehouseName, Qty = SqlFunc.AggregateSum(x.InQuantity), Amt = SqlFunc.AggregateSum(x.WarehouseAmount) });
			return await q.ToListAsync();
		}

		// 出库累计到某时点
		async Task<List<WAgg>> OutAggUntil(DateTime? until)
		{
			var q = _sqlSugarClient.Queryable<Sysoutstockorder>()
				.WhereIF(until != null, x => x.OutDate <= until)
				.WhereIF(!string.IsNullOrWhiteSpace(input.WarehouseName), x => x.WarehouseName!.Contains(input.WarehouseName!))
				.WhereIF(!string.IsNullOrWhiteSpace(input.GoodsOrCategory), x => x.GoodsName!.Contains(input.GoodsOrCategory!))
				.GroupBy(x => x.WarehouseName)
				.Select(x => new WAgg { WarehouseName = x.WarehouseName, Qty = SqlFunc.AggregateSum(x.OutQuantity), Amt = SqlFunc.AggregateSum(x.OutbouAmount) });
			return await q.ToListAsync();
		}

		var inBegin = await InAggUntil(startCut);
		var outBegin = await OutAggUntil(startCut);
		var inEnd = await InAggUntil(end);
		var outEnd = await OutAggUntil(end);

		// 期间内统计（入库、出库、盘盈）
		DateTime? periodStart = start;
		DateTime? periodEnd = end;
		var inPeriod = await _sqlSugarClient.Queryable<Sysinstockorder>()
			.WhereIF(periodStart != null, x => x.InDate >= periodStart)
			.WhereIF(periodEnd != null, x => x.InDate <= periodEnd)
			.WhereIF(!string.IsNullOrWhiteSpace(input.WarehouseName), x => x.WarehouseName!.Contains(input.WarehouseName!))
			.WhereIF(!string.IsNullOrWhiteSpace(input.GoodsOrCategory), x => x.GoodsName!.Contains(input.GoodsOrCategory!))
			.GroupBy(x => x.WarehouseName)
			.Select(x => new
			{
				x.WarehouseName,
				Times = SqlFunc.AggregateCount(x.Id),
				Qty = SqlFunc.AggregateSum(x.InQuantity),
				Amt = (decimal?)SqlFunc.AggregateSum(x.WarehouseAmount)
			})
			.ToListAsync();

		var outPeriod = await _sqlSugarClient.Queryable<Sysoutstockorder>()
			.WhereIF(periodStart != null, x => x.OutDate >= periodStart)
			.WhereIF(periodEnd != null, x => x.OutDate <= periodEnd)
			.WhereIF(!string.IsNullOrWhiteSpace(input.WarehouseName), x => x.WarehouseName!.Contains(input.WarehouseName!))
			.WhereIF(!string.IsNullOrWhiteSpace(input.GoodsOrCategory), x => x.GoodsName!.Contains(input.GoodsOrCategory!))
			.GroupBy(x => x.WarehouseName)
			.Select(x => new
			{
				x.WarehouseName,
				Times = SqlFunc.AggregateCount(x.Id),
				Qty = SqlFunc.AggregateSum(x.OutQuantity),
				Amt = (decimal?)SqlFunc.AggregateSum(x.OutbouAmount)
			})
			.ToListAsync();

		var stocktakePeriod = await _sqlSugarClient.Queryable<Sysstocktakingorder>()
			.WhereIF(periodStart != null, x => SqlFunc.IsNull(x.EndTime, SqlFunc.IsNull(x.StartTime, x.CreateTime)) >= periodStart)
			.WhereIF(periodEnd != null, x => SqlFunc.IsNull(x.EndTime, SqlFunc.IsNull(x.StartTime, x.CreateTime)) <= periodEnd)
			.WhereIF(!string.IsNullOrWhiteSpace(input.WarehouseName), x => x.WarehouseName!.Contains(input.WarehouseName!))
			.WhereIF(!string.IsNullOrWhiteSpace(input.GoodsOrCategory), x => x.GoodsName!.Contains(input.GoodsOrCategory!))
			.GroupBy(x => x.WarehouseName)
			.Select(x => new
			{
				x.WarehouseName,
				Times = SqlFunc.AggregateCount(x.Id),
				Qty = SqlFunc.AggregateSum(x.ProfitQuantity),
				Amt = (decimal?)SqlFunc.AggregateSum(x.ProfitAmount)
			})
			.ToListAsync();

		// 调拨：入库(按 ToWarehouse)、出库(按 FromWarehouse)
		// 读取仓库编码到名称映射
		var wareMaps = await _sqlSugarClient.Queryable<WarehouseModel>()
			.Select(w => new { w.WareCode, w.WareName })
			.ToListAsync();
		string ResolveWarehouseName(string? value)
		{
			if (string.IsNullOrWhiteSpace(value)) return string.Empty;
			var map = wareMaps.FirstOrDefault(m => m.WareCode == value);
			return map?.WareName ?? value; // 若匹配到编码则映射名称，否则保留原值（可能本身就是名称）
		}

		// 调拨入库（按调入仓库）内存汇总，统一名称口径
		var transferInRaw = await _sqlSugarClient.Queryable<Systransferorder>()
			.WhereIF(periodStart != null, x => x.ApplyDate >= periodStart)
			.WhereIF(periodEnd != null, x => x.ApplyDate <= periodEnd)
			.WhereIF(!string.IsNullOrWhiteSpace(input.WarehouseName), x => x.ToWarehouse!.Contains(input.WarehouseName!) || x.ToWarehouse! == input.WarehouseName)
			.WhereIF(!string.IsNullOrWhiteSpace(input.GoodsOrCategory), x => x.GoodsName!.Contains(input.GoodsOrCategory!))
			.Select(x => new { x.Id, x.ToWarehouse, x.TransferQuantity, x.TransferAmount })
			.ToListAsync();
        var transferInPeriod = transferInRaw
            .GroupBy(r => ResolveWarehouseName(r.ToWarehouse))
            .Select(g => new { WarehouseName = g.Key, Times = g.Count(), Qty = g.Sum(x => x.TransferQuantity), Amt = (decimal?)g.Sum(x => x.TransferAmount ?? 0m) })
            .ToList();

		// 调拨出库（按调出仓库）
		var transferOutRaw = await _sqlSugarClient.Queryable<Systransferorder>()
			.WhereIF(periodStart != null, x => x.ApplyDate >= periodStart)
			.WhereIF(periodEnd != null, x => x.ApplyDate <= periodEnd)
			.WhereIF(!string.IsNullOrWhiteSpace(input.WarehouseName), x => x.FromWarehouse!.Contains(input.WarehouseName!) || x.FromWarehouse! == input.WarehouseName)
			.WhereIF(!string.IsNullOrWhiteSpace(input.GoodsOrCategory), x => x.GoodsName!.Contains(input.GoodsOrCategory!))
			.Select(x => new { x.Id, x.FromWarehouse, x.TransferQuantity, x.TransferAmount })
			.ToListAsync();
        var transferOutPeriod = transferOutRaw
            .GroupBy(r => ResolveWarehouseName(r.FromWarehouse))
            .Select(g => new { WarehouseName = g.Key, Times = g.Count(), Qty = g.Sum(x => x.TransferQuantity), Amt = (decimal?)g.Sum(x => x.TransferAmount ?? 0m) })
            .ToList();

		// 货品种类：截止到 EndTime 的不同货品名称数
		var kinds = await _sqlSugarClient.Queryable<Sysinstockorder>()
			.WhereIF(end != null, x => x.InDate <= end)
			.WhereIF(!string.IsNullOrWhiteSpace(input.WarehouseName), x => x.WarehouseName!.Contains(input.WarehouseName!))
			.WhereIF(!string.IsNullOrWhiteSpace(input.GoodsOrCategory), x => x.GoodsName!.Contains(input.GoodsOrCategory!))
			.GroupBy(x => x.WarehouseName)
			.Select(x => new { x.WarehouseName, Cnt = SqlFunc.AggregateDistinctCount(x.GoodsName) })
			.ToListAsync();

        var dict = new Dictionary<string, WarehouseSummaryRow>(StringComparer.OrdinalIgnoreCase);

        // 以仓库主数据为基准，预置所有仓库（保证无业务记录也可显示）
        var baseWarehouses = await _sqlSugarClient.Queryable<WarehouseModel>()
            .WhereIF(!string.IsNullOrWhiteSpace(input.WarehouseName), w => w.WareName.Contains(input.WarehouseName!) || w.WareCode.Contains(input.WarehouseName!))
            .Select(w => new { w.WareName })
            .ToListAsync();
        foreach (var w in baseWarehouses)
        {
            var key = w.WareName ?? string.Empty;
            if (!dict.ContainsKey(key))
            {
                dict[key] = new WarehouseSummaryRow
                {
                    WarehouseName = w.WareName,
                    GoodsKinds = 0,
                    BeginAmount = 0m,
                    EndAmount = 0m,
                    InTimes = 0,
                    InQuantity = 0,
                    InAmount = 0m,
                    OutTimes = 0,
                    OutQuantity = 0,
                    OutAmount = 0m,
                    StocktakeTimes = 0,
                    StocktakeQuantity = 0,
                    StocktakeAmount = 0m,
                    TransferInTimes = 0,
                    TransferInQuantity = 0,
                    TransferInAmount = 0m,
                    TransferOutTimes = 0,
                    TransferOutQuantity = 0,
                    TransferOutAmount = 0m
                };
            }
        }

		void Apply(List<WAgg> list, bool isBegin, bool isInbound)
		{
			foreach (var a in list)
			{
				var key = a.WarehouseName ?? string.Empty;
				if (!dict.TryGetValue(key, out var row))
				{
					row = new WarehouseSummaryRow { WarehouseName = a.WarehouseName };
					dict[key] = row;
				}
				if (isBegin)
				{
					row.BeginAmount += isInbound ? a.Amt : -a.Amt;
				}
				else
				{
					row.EndAmount += isInbound ? a.Amt : -a.Amt;
				}
			}
		}

		Apply(inBegin, true, true);
		Apply(outBegin, true, false);
		Apply(inEnd, false, true);
		Apply(outEnd, false, false);

		foreach (var k in kinds)
		{
			var key = k.WarehouseName ?? string.Empty;
			if (!dict.TryGetValue(key, out var row))
			{
				row = new WarehouseSummaryRow { WarehouseName = k.WarehouseName };
				dict[key] = row;
			}
			row.GoodsKinds = k.Cnt;
		}

		// 应用期间内统计
		void ApplyPeriod<T>(IEnumerable<T> list, Action<WarehouseSummaryRow, T> setter, Func<T, string?> keySelector)
		{
			foreach (var a in list)
			{
				var key = keySelector(a) ?? string.Empty;
				if (!dict.TryGetValue(key, out var row))
				{
					row = new WarehouseSummaryRow { WarehouseName = key };
					dict[key] = row;
				}
				setter(row, a);
			}
		}

        ApplyPeriod(inPeriod, (row, a) => { row.InTimes = a.Times; row.InQuantity = a.Qty; row.InAmount = a.Amt ?? 0m; }, a => a.WarehouseName);
        ApplyPeriod(outPeriod, (row, a) => { row.OutTimes = a.Times; row.OutQuantity = a.Qty; row.OutAmount = a.Amt ?? 0m; }, a => a.WarehouseName);
        ApplyPeriod(stocktakePeriod, (row, a) => { row.StocktakeTimes = a.Times; row.StocktakeQuantity = a.Qty ?? 0; row.StocktakeAmount = a.Amt ?? 0m; }, a => a.WarehouseName);
        ApplyPeriod(transferInPeriod, (row, a) => { row.TransferInTimes = a.Times; row.TransferInQuantity = a.Qty; row.TransferInAmount = a.Amt ?? 0m; }, a => a.WarehouseName);
        ApplyPeriod(transferOutPeriod, (row, a) => { row.TransferOutTimes = a.Times; row.TransferOutQuantity = a.Qty; row.TransferOutAmount = a.Amt ?? 0m; }, a => a.WarehouseName);

		var all = dict.Values.OrderByDescending(x => x.EndAmount).ToList();
		var total = all.Count;
		var items = all.Skip((input.Page - 1) * input.PageSize).Take(input.PageSize).ToList();

		return new { result = new { items, total, page = input.Page, pageSize = input.PageSize } };
	}

	[HttpPost("chart")]
	public async Task<object> Chart([FromBody] GetWarehouseSummaryInput input)
	{
		var page = await Page(input) as dynamic;
		var items = (page?.result?.items as IEnumerable<WarehouseSummaryRow>) ?? Enumerable.Empty<WarehouseSummaryRow>();
		var x = items.Select(i => i.WarehouseName ?? string.Empty).ToList();
		var begin = items.Select(i => i.BeginAmount).ToList();
		var end = items.Select(i => i.EndAmount).ToList();
		return new { result = new { x, begin, end } };
	}
}

public class GetWarehouseSummaryInput
{
	public DateTime? StartTime { get; set; }
	public DateTime? EndTime { get; set; }
	public string? WarehouseName { get; set; }
	public string? GoodsOrCategory { get; set; }
	public int Page { get; set; } = 1;
	public int PageSize { get; set; } = 10;
}

public class WarehouseSummaryRow
{
	public string? WarehouseName { get; set; }
	public int GoodsKinds { get; set; }
	public decimal BeginAmount { get; set; }
	public decimal EndAmount { get; set; }

    // 期间内：入库
    public int InTimes { get; set; }
    public int InQuantity { get; set; }
    public decimal InAmount { get; set; }

    // 期间内：出库
    public int OutTimes { get; set; }
    public int OutQuantity { get; set; }
    public decimal OutAmount { get; set; }

    // 期间内：盘盈（次数/数量/金额）
    public int StocktakeTimes { get; set; }
    public int StocktakeQuantity { get; set; }
    public decimal StocktakeAmount { get; set; }

    // 期间内：调拨入库
    public int TransferInTimes { get; set; }
    public int TransferInQuantity { get; set; }
    public decimal TransferInAmount { get; set; }

    // 期间内：调拨出库
    public int TransferOutTimes { get; set; }
    public int TransferOutQuantity { get; set; }
    public decimal TransferOutAmount { get; set; }
}

internal class WAgg
{
	public string? WarehouseName { get; set; }
	public int Qty { get; set; }
	public decimal Amt { get; set; }
}


